package com.hongji.lms5xxcommunicator.log;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * <p>
 * 日志切面：统一拦截 Controller 层方法调用，记录入参、执行耗时、返回值及异常信息。
 * </p>
 *
 * <p><b>职责</b></p>
 * <ul>
 *   <li>在进入方法时打印参数。</li>
 *   <li>在方法正常返回时打印返回值与耗时。</li>
 *   <li>在方法异常时打印异常堆栈。</li>
 * </ul>
 *
 * <p><b>用法</b></p>
 * <ol>
 *   <li>所有 {@code com.hongji.lms5xxcommunicator.controller..*} 包下的公开方法都会被拦截。</li>
 *   <li>无需在 Controller 类中手工添加日志，统一由切面处理。</li>
 *   <li>如需扩展，可在切点表达式中增加或缩小拦截范围。</li>
 * </ol>
 *
 * @see org.aspectj.lang.annotation.Aspect
 * @see org.aspectj.lang.annotation.Around
 */
@Aspect
@Component
public class LoggingAspect {

    private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);

    /**
     * <p>切点定义：拦截 controller 包及其子包下所有方法。</p>
     */
    @Pointcut("execution(* com.hongji.lms5xxcommunicator.controller..*.*(..))")
    public void controllerLayer() {}

    /**
     * <p>环绕通知：记录方法调用日志。</p>
     *
     * <ul>
     *   <li>进入方法时打印参数。</li>
     *   <li>正常返回时打印耗时与返回值。</li>
     *   <li>发生异常时打印堆栈。</li>
     * </ul>
     *
     * @param joinPoint 方法执行上下文
     * @return 方法执行结果
     * @throws Throwable 目标方法可能抛出的任何异常
     */
    @Around("controllerLayer()")
    public Object logMethodCall(ProceedingJoinPoint joinPoint) throws Throwable {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();

        Object[] args = joinPoint.getArgs();
        // 避免直接打印 Object[] 产生 [Ljava.lang.Object;@xxx，转成字符串
        String argStr = (args == null || args.length == 0) ? "[]" : Arrays.deepToString(args);

        log.info("[Entry] {}.{}() - Args: {}", className, methodName, argStr);

        long startTime = System.currentTimeMillis();
        try {
            Object result = joinPoint.proceed();
            long duration = System.currentTimeMillis() - startTime;

            // 返回值可能很大，限制长度避免刷屏
            String resultStr = (result == null) ? "null" : String.valueOf(result);
            if (resultStr.length() > 500) {
                resultStr = resultStr.substring(0, 500) + "...(truncated)";
            }

            log.info("[Exit] {}.{}() - Time: {}ms - Result: {}", className, methodName, duration, resultStr);
            return result;
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("[Exception] {}.{}() - Time: {}ms", className, methodName, duration, e);
            throw e;
        }
    }
}
